#if !defined(TUTILS_H_INCLUDED)
#define TUTILS_H_INCLUDED

#pragma once

namespace tutil
{
	template<class T>
	T SetInRange(const T &x, const T &min, const T &max) { return ( (x) < (min) ? (min) : ( (x) > (max) ? (max) : (x) ) ); }

	template<typename T>
	struct ExtractSubStringT
	{
		// tstring version of AfxExtractSubString()
		BOOL operator () (std::basic_string<T> &rString, const T *lpszFullString, int iSubString, T chSep)
		{
			if (lpszFullString == NULL)
				return FALSE;
			
			while (iSubString--)
			{
				lpszFullString = _tcschr(lpszFullString, chSep);
				if (lpszFullString == NULL)
				{
					//	rString.erase(rString.begin(), rString.end());        // return empty string as well
					rString.erase();
					return FALSE;
				}
				lpszFullString++;       // point past the separator
			}
			T *lpchEnd = _tcschr(lpszFullString, chSep);
			int nLen = (lpchEnd == NULL) ?
				_tcslen(lpszFullString) : (int)(lpchEnd - lpszFullString);
			rString.reserve(nLen);
			rString.assign(lpszFullString, nLen);
			
			return TRUE;
		}
	};

	template<>
	struct ExtractSubStringT<WCHAR>
	{
		BOOL operator() (std::basic_string<WCHAR> &rString, LPCWSTR lpszFullString, int iSubString, WCHAR chSep)
		{
			if (lpszFullString == NULL)
				return FALSE;
			
			while (iSubString--)
			{
				lpszFullString = wcschr(lpszFullString, chSep);
				if (lpszFullString == NULL)
				{
					//	rString.erase(rString.begin(), rString.end());        // return empty string as well
					rString.erase();
					return FALSE;
				}
				lpszFullString++;       // point past the separator
			}
			LPWSTR lpchEnd = wcschr(lpszFullString, chSep);
			int nLen = (lpchEnd == NULL) ?
				wcslen(lpszFullString) : (int)(lpchEnd - lpszFullString);
			rString.reserve(nLen);
			rString.assign(lpszFullString, nLen);
			
			return TRUE;
		}
	};
	
	template<>
	struct ExtractSubStringT<CHAR>
	{
		BOOL operator() (std::basic_string<CHAR> &rString, LPCSTR lpszFullString, int iSubString, CHAR chSep)
		{
			if (lpszFullString == NULL)
				return FALSE;
			
			while (iSubString--)
			{
				lpszFullString = strchr(lpszFullString, chSep);
				if (lpszFullString == NULL)
				{
					//	rString.erase(rString.begin(), rString.end());        // return empty string as well
					rString.erase();
					return FALSE;
				}
				lpszFullString++;       // point past the separator
			}
			LPSTR lpchEnd = strchr(lpszFullString, chSep);
			int nLen = (lpchEnd == NULL) ?
				strlen(lpszFullString) : (int)(lpchEnd - lpszFullString);
			rString.reserve(nLen);
			rString.assign(lpszFullString, nLen);
			
			return TRUE;
		}
	};

	template<typename T>
	struct ErrorStringT
	{
		std::basic_string<T> operator() (DWORD dwErr)
		{
			std::basic_string<T> Error;
			T *s = NULL;
			if (::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
				NULL,										// no source buffer needed
				dwErr,										// error code for this message
				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),	// default language ID
				(T *)&s,									// allocated by fcn
				0,											// Minimum size of buffer
				NULL										// no inserts
				) == 0)
			{ /* failed */
				// Unknown error code
				std::basic_ostringstream<T> oss;
				oss << _T("Unknown error 0x")
					<< std::setfill( _T('0') ) << std::setw(8) << std::hex << ((WORD)((DWORD_PTR)(dwErr) & 0xffff))
					<< std::endl;
				Error = oss.str().c_str();
			} /* failed */
			else
			{ /* success */
				T *p = _tcschr(s, _T('\r'));
				if (p != NULL)
				{ /* lose CRLF */
					*p = _T('\0');
				} /* lose CRLF */
				Error = s;
				::LocalFree(s);
			} /* success */
			return Error;
		} // ErrorString
	};

	template<>
	struct ErrorStringT<WCHAR>
	{
		std::basic_string<WCHAR> operator() (DWORD dwErr)
		{
			std::basic_string<WCHAR> Error;
			LPWSTR s = NULL;
			if (::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
				NULL,										// no source buffer needed
				dwErr,										// error code for this message
				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),	// default language
				(LPWSTR)&s,									// allocated by fcn
				0,											// Minimum size of buffer
				NULL										// no inserts
				) == 0)
			{ /* failed */
				// Unknown error code
				std::basic_ostringstream<WCHAR> oss;
				oss << L"Unknown error 0x"
					<< std::setfill( L'0' ) << std::setw(8) << std::hex << ((WORD)((DWORD_PTR)(dwErr) & 0xffff))
					<< std::endl;
				Error = oss.str().c_str();
			} /* failed */
			else
			{ /* success */
				LPWSTR p = wcschr(s, L'\r');
				if (p != NULL)
				{ /* lose CRLF */
					*p = L'\0';
				} /* lose CRLF */
				Error = s;
				::LocalFree(s);
			} /* success */
			return Error;
		} // ErrorString
	};
	
	template<>
	struct ErrorStringT<CHAR>
	{
		std::basic_string<CHAR> operator() (DWORD dwErr)
		{
			std::basic_string<CHAR> Error;
			LPSTR s = NULL;
			if (::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
				NULL,										// no source buffer needed
				dwErr,										// error code for this message
				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),	// default language
				(LPSTR)&s,									// allocated by fcn
				0,											// Minimum size of buffer
				NULL										// no inserts
				) == 0)
			{ /* failed */
				// Unknown error code
				std::basic_ostringstream<CHAR> oss;
				oss << "Unknown error 0x"
					<< std::setfill( '0' ) << std::setw(8) << std::hex << ((WORD)((DWORD_PTR)(dwErr) & 0xffff))
					<< std::endl;
				Error = oss.str().c_str();
			} /* failed */
			else
			{ /* success */
				LPSTR p = strchr(s, '\r');
				if (p != NULL)
				{ /* lose CRLF */
					*p = '\0';
				} /* lose CRLF */
				Error = s;
				::LocalFree(s);
			} /* success */
			return Error;
		} // ErrorString
	};

	#define ExtractSubStringW	ExtractSubStringT<WCHAR>()
	#define ExtractSubStringA	ExtractSubStringT<CHAR>()
	#define ExtractSubString	ExtractSubStringT<TCHAR>()

	#define ErrorStringW	ErrorStringT<WCHAR>()
	#define ErrorStringA	ErrorStringT<CHAR>()
	#define ErrorString		ErrorStringT<TCHAR>()
};	// namespace tutil

#endif	// #if !defined(TUTILS_H_INCLUDED)